import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { hexToNum, hexToStr, hexToSingle, numToHex, strToHex } from 'src/util';
import * as mqtt from 'mqtt'

import { InjectConnection } from '@nestjs/mongoose';
import { Connection, Mongoose } from 'mongoose';
import * as mongoose from 'mongoose';

import DayLogSchema from './schema/DayLog'
import DeviceHeartSchema from './schema/DeviceHeart'
import ChargeOrderRealTimeInfoSchema from './schema/ChargeOrderRealTimeInfo'
import ChargeOrderFinishMsgSchema from './schema/ChargeOrderFinishMsg'
import ChargeResponseMsgSchema from './schema/ChargeResponseMsg'
import { DeviceModelService } from 'src/model/device-model/device-model.service';
import { RedisService } from 'src/redis/redis.service';
import { IccardService } from 'src/module/iccard/iccard.service';
import { CRC } from 'crc-full';

interface Message {
    cmd : string;
    deviceId : string;
    timestamp : string;
    data : string;
    type : string;
    packDir : string;
}


@Injectable()
export class MessageService {
    private crc:CRC;
    private deviceHeartCollectionName:string;
    private chargeOrderRealTimeInfoCollectionName:string;
    private chargeOrderFinishMsgCollectionName:string;
    private chargeResponseMsgCollectionName:string;

    private platformResponseMsgTopic:string;

    private icardChargeOrderMessageKey:string; //刷卡的消息队列

    private connection:Mongoose;

    private client:mqtt.MqttClient;
    constructor(
        private configService:ConfigService,
        private deviceModelService:DeviceModelService,
        private redis:RedisService,
        private iccardService:IccardService
        //@InjectConnection() private connection: Connection
    ) {

        this.crc = new CRC("CRC16_XMODEM", 16, 0x1021, 0x0000, 0x0000, false, false);

        const mongodbCollection = this.configService.get('mongodbCollection')
        this.deviceHeartCollectionName = mongodbCollection.deviceHeart
        this.chargeOrderRealTimeInfoCollectionName = mongodbCollection.chargeOrderRealTimeInfo
        this.chargeOrderFinishMsgCollectionName = mongodbCollection.chargeOrderFinishMsg
        this.chargeResponseMsgCollectionName = mongodbCollection.chargeResponseMsg

        this.icardChargeOrderMessageKey = this.configService.get<string>('cacheKey.icardChargeOrderMessage') //刷卡的信息队列

        mongoose.connect(this.configService.get('mongodb.uri'), {useNewUrlParser : true, useUnifiedTopology: true, poolSize : 5}).then(e => {
            console.log('mongo连接成功')
            this.connection = e
        }).catch(error => {
            console.log('mongo连接失败')
            console.log(error)
        })



        const mqttConf = this.configService.get('mqtt')
        this.platformResponseMsgTopic = mqttConf.topic.platformResponseMsg
        
        this.client = mqtt.connect(mqttConf.options)
        this.client.on('connect', () => {
            console.log('mqtt连接成功')
            this.client.subscribe(mqttConf.topic.deviceReportMsg)
        })
        this.client.on('error', () => {
            console.log('mqtt error')
        })
        this.client.on('message', (topic, payload) => {
            let message:Message = JSON.parse(payload.toString())

            const cmd = message.cmd
            const deviceId = message.deviceId
            const simCardNo = hexToStr(deviceId)

            if(cmd == '0000') {
                this.offline(simCardNo)
                return
            }


            
            const packDir = message.packDir
            const type = message.type

            

            const date = new Date()
            const logCollectionName = 'log_' + date.getFullYear() + (date.getMonth() + 1) + date.getDate() + '_' + cmd

            //解析时间戳
            const timestamp = Number(hexToNum(message.timestamp.substr(0, 8)) + '' + hexToNum(message.timestamp.substr(8)))
            
            
            const time = date.getTime()

            this.connection.model(logCollectionName, DayLogSchema, logCollectionName).insertMany({ simCardNo, timestamp, packDir, type, createTime : time, data : message.data})

            this.connection.model(this.deviceHeartCollectionName, DeviceHeartSchema, this.deviceHeartCollectionName).updateOne({ simCardNo : simCardNo}, {$set : { timestamp, updateTime : time}}, {upsert : true}, (a, b) => {})

            if(cmd == '0000') { //设备掉线

            }

            
            if(cmd == '0001') { //注册
                console.log('注册')
                this.register(cmd, deviceId, simCardNo, type)
                return
            }

            if(cmd == '0002') { //设备向后台校核当前费率设置，废弃
                return
            }

            if(cmd == '0003') { //心跳
                console.log('心跳')
                this.heart(cmd, deviceId, simCardNo, type)
                return
            }

            if(cmd === '0004') { //订单实时信息，不需要回复
                console.log('订单实时信息')
                this.chargeOrderRealTimeInfo(message.data, timestamp, time)
                return
            }

            if(cmd == '0005') { //充电结束订单上报
                this.chargeOrderFinish(cmd, deviceId, type, message.data, time)
                return
            }

            if(cmd == '0006') { //后台下发费率设置，设备响应结果
                return
            }

            if(cmd == '0007') { //后台查询设备运营情况,设备响应结果

                return
            }

            if(cmd == '0008') { //后台扫码充电
                this.chargeResponse(simCardNo, type, message.data, time)
                return
            }

            if(cmd == '0009') { //设备上报线上卡操作

            }

            if(cmd == '000A') { //CMD_SERVER_CTRL 后台远程控制业务,设备响应
                return
            }

            if(cmd == '000B') { //CMD_DEV_STAT_REP 设备各种状态上报
                this.deviceReportData(cmd, deviceId, type)
                return
            }

            if(cmd == '000C') { //设备请求服务
                this.deviceRequest(cmd, deviceId, type, message.data)
            }
        })

    }

    run() {
        this.redis.redisClient.brpop(this.icardChargeOrderMessageKey, 5, (error, result) => {
            try {
                if(error == null && result !== null) {
                    const { cmd, data} = JSON.parse(result[1])
                    
                } 
            } catch (error) {
                
            }

            this.run()
        })
    }

    response(deviceId:string, packDir:'00'|'01', type:string, str:string) {
        let msg = '2878' + packDir + type
        let d = new Date()
        let time = d.getTime()
        msg += numToHex(Math.floor(time / 1000), 8)
        msg += numToHex(time % 1000, 4)



        let followData = '00' + '0000000000000000' + deviceId + str; // + '0000' + '1555'
        let length = numToHex(followData.length / 2 + 4, 4)

        msg += (length + followData)

        msg += (this.crc.compute(Buffer.from(msg, 'hex')).toString(16))

        this.client.publish(this.platformResponseMsgTopic, JSON.stringify({deviceId : deviceId, body : msg + '1555'}), {qos : 2})
    }

    //离线
    offline(simCardNo:string) {
        this.deviceModelService.update({simCardNo : simCardNo}, {connectStatus : 0})
    }

    //注册
    register(cmd:string, deviceId:string, simCardNo:string, type:string) {
        let str = cmd + numToHex(1, 2)
        this.response(deviceId, '01', type, str)
        this.deviceModelService.update({simCardNo : simCardNo}, {connectStatus : 1})
    }

    //心跳
    heart(cmd:string, deviceId:string, simCardNo:string, type:string) {
        this.response(deviceId, '01', type, cmd)
        this.deviceModelService.update({simCardNo : simCardNo}, {connectStatus : 1})
    }

    //发起充电，增加充电，停止充电，设备响应
    chargeResponse(simCardNo:string, type:string, msg:string, time:number) {
        const portNo = hexToNum(msg.substr(0, 2))
        const orderId = hexToNum(msg.substr(2, 22))
        const action = hexToNum(msg.substr(24, 2))
        const status = hexToNum(msg.substr(26, 2))

        this.connection.model(this.chargeResponseMsgCollectionName, ChargeResponseMsgSchema, this.chargeResponseMsgCollectionName)
        .insertMany({type : hexToNum(type), simCardNo, portNo, orderId, action, status, createTime : time, data : msg})
    }

    //设备上报实时的订单信息
    chargeOrderRealTimeInfo(data:string, timestamp:number, time:number) {
        const orderId = hexToNum(data.substr(2, 22))
        const infoData = {
            timestamp : timestamp,
            updateTime:time,
            consumeTime : hexToNum(data.substr(24, 8)),
            power : hexToSingle(data.substr(32, 8)),
            voltage : hexToSingle(data.substr(40, 8)),
            current : hexToSingle(data.substr(48, 8)),
            consumeEnergy : hexToSingle(data.substr(56, 8)),
            temperature : hexToNum(data.substr(64, 2)),
        }

        this.connection.model(this.chargeOrderRealTimeInfoCollectionName, ChargeOrderRealTimeInfoSchema, this.chargeOrderRealTimeInfoCollectionName)
        .updateOne({ orderId : orderId}, {$set : {lastData : infoData}, $push : { data : infoData}}, {upsert : true}, (a, b) => {

        })
    }

    //充电完成，设备上报订单信息
    chargeOrderFinish(cmd:string, deviceId:string, type:string, msg:string, time:number) {
        const orderId = hexToNum(msg.substr(2, 22))

        this.connection.model(this.chargeOrderFinishMsgCollectionName, ChargeOrderFinishMsgSchema, this.chargeOrderFinishMsgCollectionName)
        .insertMany({ orderId, createTime : time, data : msg})

        this.response(deviceId, '01', type, cmd + msg.substr(0, 28) + numToHex(1, 2))
    }

    //设备上报各种状态
    deviceReportData(cmd:string, deviceId:string, type:string) {
        this.response(deviceId, '01', type, cmd + numToHex(1, 2))
    }


    //设备各种请求
    async deviceRequest(cmd:string, deviceId:string, type:string, msg:string) {
        const reqId = msg.substr(2, 2)
        const portNo = hexToNum(msg.substr(4, 2))

        if(reqId == '01') { //请求设备编号
            this.deviceModelService.findOne({simCardNo : hexToStr(deviceId)}, ['deviceId']).then(device => {
                if(device) {
                    //this.response(deviceId, '01', type, )
                }
            })
        }else if(reqId == '02') { //请求设备编号
            this.deviceModelService.findOne({simCardNo : hexToStr(deviceId)}, ['merchantId']).then(device => {
                if(device) {
                    //this.response(deviceId, '01', type, )
                }
            })
        }else if(reqId == '03') { //请求服务器时间戳

        }else if(reqId == '04') {

        }


        
        
        let device = await this.deviceModelService.findOne({simCardNo : hexToStr(deviceId)}, ['deviceId'])

    }

    async iccard(cmd:string, deviceId:string, type:string, msg:string) {
        const cardId = msg.substr(0, 8)
        const action = msg.substr(8, 2)
        const cardNo = hexToStr(cardId)
        if(action == '01') { //查询余额
            this.iccardService.balance(cardNo).then(card => {
                let str = cmd + cardId + action
                if(!card || card.status == 2) { //2是挂失
                    str += '01'
                }else if(card.status == 0) { //未激活
                    str += '04'
                }else if(card.status == 3) { //黑名单
                    str += '02'
                }else if(card.status == 1) { //正常使用中的卡
                    str += '00'
                    str += numToHex(card.balace, 8)
                }
                
                this.response(deviceId, '01', type, str)
            })
        }else if(action == '02') { //充电
            let str = cmd + cardId + action
            const portNo = msg.substr(10, 2)
            const res = await this.iccardService.charge(hexToStr(deviceId), cardNo, hexToNum(portNo))
            if(res == false) {
                str += '03'
            }else if(res.error == '00') {
                str += '00'
                str += numToHex(res.balance * 100, 8)
                str += portNo
                str += strToHex(res.orderNo, 22)
            }else {
                str += res.error
            }
            this.response(deviceId, '01', type, str)
        }else if(action == '03') { //停止充电，结算订单

        }
    }
    
}
